#include <user.h>
#include <ts7200.h>
#include <defs.h>
#include <vic.h>
#include <uart.h>
#include <iobuf.h>
#include <isr.h>
#include <train.h>
#include <messages.h>
#include <bwio.h>
#include <io.h>

void TrainRxNotifier() {
  int i, c, ptr;
  rx_req msg;
  msg.type = MSG_RX_DATA;

  uart *const uart1 = (uart*)( UART1_BASE );
 
  int server_tid;
  Receive(&server_tid, NULL, 0);
  Reply(server_tid, NULL, 0);

  for ( ; ; ) {
    int int_flags = AwaitEvent(EVENT_UART1_RX, NULL, 0);
    ptr = 0;
    if (int_flags & RIS_MASK) {
      // read 8 bytes from buffer
      for (i = 0; i < 8; i++) {
        c = uart1->data;
        if (uart1->rx_sts & (FE_MASK | PE_MASK | BE_MASK | OE_MASK)) {
          bwprintf(COM2, "RX error: %x\r\n", uart1->rx_sts);
          Panic("TerminalRxNotifier", "RX error bits set");
        }
        msg.ch[ptr++] = c;
      }
    } else if (int_flags & RTIS_MASK) {
      // read from FIFO until empty
      while (!(uart1->flag & RXFE_MASK)) {
        c = uart1->data;
        if (uart1->rx_sts & (FE_MASK | PE_MASK | BE_MASK | OE_MASK)) {
          bwprintf(COM2, "RX error: %x\r\n", uart1->rx_sts);
          Panic("TerminalRxNotifier", "RX error bits set");
        }
        msg.ch[ptr++] = c;
      }
    }
    msg.len = ptr;
    Send( server_tid, (char*)&msg, sizeof(rx_req), NULL, 0 );
  }
  Panic("TrainRxNotifier", "Chuck Norris!");
}

void TrainRxServer() {
  int i, i_min;
  iobuf buf;
  IobufInit(&buf);

  // Getc() queue
  iobuf request_queue;
  IobufInit(&request_queue);
  
  rx_req msg;  
  rx_reply reply;
 
  int sender_tid;
  uart *const uart1 = (uart*)( UART1_BASE );

  int notifier_tid = Create(8, &TrainRxNotifier, "TrainRxNotifier");

  // notify parent
  int parent_tid;
  Receive(&parent_tid, NULL, 0);
  Reply(parent_tid, NULL, 0);
  
  Send(notifier_tid, NULL, 0, NULL, 0);
  
  RegisterAs("train-rx-server");

  for ( ; ; ) {
    Receive(&sender_tid, (char*)&msg, sizeof(rx_req));
    switch (msg.type) {
    case MSG_RX_DATA:
      Reply(notifier_tid, NULL, 0);

      if (IobufFull(&buf))
        Panic("TrainRxServer", "IO buffer full");

      // courier has data for me
      for (i = 0; i < msg.len; i++) {
        IobufEnqueue( &buf, msg.ch[i] );
      }
      if (!IobufEmpty(&request_queue)) {
        IobufDequeue(&request_queue, &i_min);
        IobufDequeue(&buf, &(reply.ch));
        Reply( i_min, (char*)&reply, sizeof(rx_reply) );
      }
      break;
    case MSG_RX_HASC:
      reply.ch = !IobufEmpty(&buf);
      Reply( sender_tid, (char*)&reply, sizeof(rx_reply) );
      break;
    case MSG_RX_GETC:
      IobufEnqueue(&request_queue, sender_tid);
      if (!IobufEmpty(&buf)) {
        IobufDequeue(&request_queue, &i_min);
        IobufDequeue(&buf, &(reply.ch));
        Reply( i_min, (char*)&reply, sizeof(rx_reply) );
      }
      break;
    }
    uart1->ctrl |= (RIEN_MASK | RTIEN_MASK);
  }
  Panic("TrainRxServer", "Chuck Norris!");
}

void TrainTxNotifier() {
  // notify parent
  int server_tid;
  Receive(&server_tid, NULL, 0);
  Reply(server_tid, NULL, 0);
  
  uart *const uart1 = (uart*)( UART1_BASE );
 
  for ( ; ; ) {
    AwaitEvent( EVENT_UART1_TX, NULL, 0 );
    if (uart1->flag & CTS_MASK)
      Send(server_tid, NULL, 0, NULL, 0);
  }
  Panic("TrainTxNotifier", "Chuck Norris!");
}

void TrainTxServer() {
  int c;
  iobuf buf;
  IobufInit(&buf);
 
  train_tx_reply reply;
  train_tx_req msg;
  int clear_to_send = TRUE;
 
  int sender_tid;
  uart *const uart1 = (uart*)( UART1_BASE );  
  
  int notifier_tid = Create(9, &TrainTxNotifier, "TrainTxNotifier");

  // notify parent
  int parent_tid;
  Receive(&parent_tid, NULL, 0);
  Reply(parent_tid, NULL, 0);
  
  Send(notifier_tid, NULL, 0, NULL, 0);
 
  RegisterAs("train-tx-server");
   
  for ( ; ; ) {
    Receive( &sender_tid, (char*)&msg, sizeof(train_tx_req));
    
    if (sender_tid == notifier_tid) {
      // notifier sends CTS
      clear_to_send = TRUE;
      if (!IobufEmpty(&buf)) {         
        IobufDequeue( &buf, &c );
        uart1->data = c;   // send data
        clear_to_send = FALSE;
      }
      Reply(notifier_tid, NULL, 0);
    } else {
      // system call request received!
      if (IobufFull(&buf)) {
        while (!IobufEmpty(&buf)) {
          int a;
          IobufDequeue(&buf, &a);
          bwprintf(COM2, "buf: %d\r\n", a);
        }
        Panic("TrainTxServer", "IO buffer full");
      }
      IobufEnqueue(&buf, msg.ch);
      if (clear_to_send) {         
        IobufDequeue( &buf, &c );
        uart1->data = c;   // send data
        clear_to_send = FALSE;
      }
      Reply( sender_tid, NULL, 0 );
    }
  }
  Panic("TrainTxServer", "Chuck Norris!");
}

